home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
fish
/
001-100
/
001-025
/
023
/
ver30
/
search.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-17
|
14KB
|
673 lines
/*
* Name: MicroEMACS
* Search commands.
* Version: 30
* Last edit: 14-Feb-86
* By: rex::conroy, rex::ellison
* decvax!decwrl!dec-rhea!dec-rex!conroy
* ...!dec-vox!ellison
*
* The functions in this file implement the
* search commands (both plain and incremental searches
* are supported) and the query-replace command.
*
* The plain old search code is part of the original
* MicroEMACS "distribution". The incremental search code,
* and the query-replace code, is by Rich Ellison.
*/
#include "def.h"
#define CCHR(x) ((x)-'@')
#define SRCH_BEGIN (0) /* Search sub-codes. */
#define SRCH_FORW (-1)
#define SRCH_BACK (-2)
#define SRCH_PREV (-3)
#define SRCH_NEXT (-4)
#define SRCH_NOPR (-5)
#define SRCH_ACCM (-6)
typedef struct {
int s_code;
LINE *s_dotp;
int s_doto;
} SRCHCOM;
static SRCHCOM cmds[NSRCH];
static int cip;
int srch_lastdir = SRCH_NOPR; /* Last search flags. */
/*
* Search forward.
* Get a search string from the user, and search for it,
* starting at ".". If found, "." gets moved to just after the
* matched characters, and display does all the hard stuff.
* If not found, it just prints a message.
*/
forwsearch(f, n, k)
{
register int s;
if ((s=readpattern("Search")) != TRUE)
return (s);
if (forwsrch() == FALSE) {
eprintf("Not found");
return (FALSE);
}
srch_lastdir = SRCH_FORW;
return (TRUE);
}
/*
* Reverse search.
* Get a search string from the user, and search, starting at "."
* and proceeding toward the front of the buffer. If found "." is left
* pointing at the first character of the pattern [the last character that
* was matched].
*/
backsearch(f, n, k)
{
register int s;
if ((s=readpattern("Reverse search")) != TRUE)
return (s);
if (backsrch() == FALSE) {
eprintf("Not found");
return (FALSE);
}
srch_lastdir = SRCH_BACK;
return (TRUE);
}
/*
* Search again, using the same search string
* and direction as the last search command. The direction
* has been saved in "srch_lastdir", so you know which way
* to go.
*/
searchagain(f, n, k)
{
if (srch_lastdir == SRCH_FORW) {
if (forwsrch() == FALSE) {
eprintf("Not found");
return (FALSE);
}
return (TRUE);
}
if (srch_lastdir == SRCH_BACK) {
if (backsrch() == FALSE) {
eprintf("Not found");
return (FALSE);
}
return (TRUE);
}
eprintf("No last search");
return (FALSE);
}
/*
* Use incremental searching, initially in the forward direction.
* isearch ignores any explicit arguments.
*/
forwisearch(f, n, k)
{
return (isearch(SRCH_FORW));
}
/*
* Use incremental searching, initially in the reverse direction.
* isearch ignores any explicit arguments.
*/
backisearch(f, n, k)
{
return (isearch(SRCH_BACK));
}
/*
* Incremental Search.
* dir is used as the initial direction to search.
* ^N find next occurance (if first thing typed reuse old string).
* ^P find prev occurance (if first thing typed reuse old string).
* ^S switch direction to forward, find next
* ^R switch direction to reverse, find prev
* ^Q quote next character (allows searching for ^N etc.)
* <ESC> exit from Isearch.
* <DEL> undoes last character typed. (tricky job to do this correctly).
* else accumulate into search string
*/
isearch(dir)
{
register int c;
register LINE *clp;
register int cbo;
register int success;
int pptr;
for (cip=0; cip<NSRCH; cip++)
cmds[cip].s_code = SRCH_NOPR;
cip = 0;
pptr = -1;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
is_lpush();
is_cpush(SRCH_BEGIN);
success = TRUE;
is_prompt(dir, TRUE, success);
for (;;) {
update();
switch (c = ttgetc()) {
case CCHR('M'):
case METACH:
srch_lastdir = dir;
eprintf("[Done]");
return (TRUE);
case CCHR('G'):
curwp->w_dotp = clp;
curwp->w_doto = cbo;
curwp->w_flag |= WFMOVE;
srch_lastdir = dir;
ctrlg(FALSE, 0, KRANDOM);
return (FALSE);
case CCHR('S'):
case CCHR('F'):
if (dir == SRCH_BACK) {
dir = SRCH_FORW;
is_lpush();
is_cpush(SRCH_FORW);
success = TRUE;
}
/* Drop through to find next. */
case CCHR('N'):
if (success==FALSE && dir==SRCH_FORW)
break;
is_lpush();
forwchar(FALSE, 1, KRANDOM);
if (is_find(SRCH_NEXT) != FALSE) {
is_cpush(SRCH_NEXT);
pptr = strlen(pat);
} else {
backchar(FALSE, 1, KRANDOM);
ttbeep();
success = FALSE;
}
is_prompt(dir, FALSE, success);
break;
case CCHR('R'):
case CCHR('B'):
if (dir == SRCH_FORW) {
dir = SRCH_BACK;
is_lpush();
is_cpush(SRCH_BACK);
success = TRUE;
}
/* Drop through to find previous. */
case CCHR('P'):
if (success==FALSE && dir==SRCH_BACK)
break;
is_lpush();
backchar(FALSE, 1, KRANDOM);
if (is_find(SRCH_PREV) != FALSE) {
is_cpush(SRCH_PREV);
pptr = strlen(pat);
} else {
forwchar(FALSE, 1, KRANDOM);
ttbeep();
success = FALSE;
}
is_prompt(dir,FALSE,success);
break;
case 0x7F:
if (is_undo(&pptr, &dir) != TRUE)
return (ABORT);
if (is_peek() != SRCH_ACCM)
success = TRUE;
is_prompt(dir, FALSE, success);
break;
case CCHR('^'):
case CCHR('Q'):
c = ttgetc();
case CCHR('U'):
case CCHR('X'):
case CCHR('J'):
goto addchar;
default:
if (ISCTRL(c) != FALSE) {
c += '@';
c |= KCTRL;
success = execute(c, FALSE, 1);
curwp->w_flag |= WFMOVE;
return (success);
}
addchar:
if (pptr == -1)
pptr = 0;
if (pptr == 0)
success = TRUE;
pat[pptr++] = c;
if (pptr == NPAT) {
eprintf("Pattern too long");
ctrlg(FALSE, 0, KRANDOM);
return (ABORT);
}
pat[pptr] = '\0';
is_lpush();
if (success != FALSE) {
if (is_find(dir) != FALSE)
is_cpush(c);
else {
success = FALSE;
ttbeep();
is_cpush(SRCH_ACCM);
}
} else
is_cpush(SRCH_ACCM);
is_prompt(dir, FALSE, success);
}
}
}
is_cpush(cmd)
register int cmd;
{
if (++cip >= NSRCH)
cip = 0;
cmds[cip].s_code = cmd;
}
is_lpush()
{
register int ctp;
ctp = cip+1;
if (ctp >= NSRCH)
ctp = 0;
cmds[ctp].s_code = SRCH_NOPR;
cmds[ctp].s_doto = curwp->w_doto;
cmds[ctp].s_dotp = curwp->w_dotp;
}
is_pop()
{
if (cmds[cip].s_code != SRCH_NOPR) {
curwp->w_doto = cmds[cip].s_doto;
curwp->w_dotp = cmds[cip].s_dotp;
curwp->w_flag |= WFMOVE;
cmds[cip].s_code = SRCH_NOPR;
}
if (--cip <= 0)
cip = NSRCH-1;
}
is_peek()
{
if (cip == 0)
return (cmds[NSRCH-1].s_code);
else
return (cmds[cip-1].s_code);
}
is_undo(pptr, dir)
register int *pptr;
register int *dir;
{
switch (cmds[cip].s_code) {
case SRCH_NOPR:
case SRCH_BEGIN:
case SRCH_NEXT:
case SRCH_PREV:
break;
case SRCH_FORW:
*dir = SRCH_BACK;
break;
case SRCH_BACK:
*dir = SRCH_FORW;
break;
case SRCH_ACCM:
default:
*pptr -= 1;
if (*pptr < 0)
*pptr = 0;
pat[*pptr] = '\0';
break;
}
is_pop();
return (TRUE);
}
is_find(dir)
register int dir;
{
register int plen;
plen = strlen(pat);
if (plen != 0) {
if (dir==SRCH_FORW || dir==SRCH_NEXT) {
backchar(FALSE, plen, KRANDOM);
if (forwsrch() == FALSE) {
forwchar(FALSE, plen, KRANDOM);
return (FALSE);
}
return (TRUE);
}
if (dir==SRCH_BACK || dir==SRCH_PREV) {
forwchar(FALSE, plen, KRANDOM);
if (backsrch() == FALSE) {
backchar(FALSE, plen, KRANDOM);
return (FALSE);
}
return (TRUE);
}
eprintf("bad call to is_find");
ctrlg(FALSE, 0, KRANDOM);
return (FALSE);
}
return (FALSE);
}
/*
* If called with "dir" not one of SRCH_FORW
* or SRCH_BACK, this routine used to print an error
* message. It also used to return TRUE or FALSE,
* depending on if it liked the "dir". However, none
* of the callers looked at the status, so I just
* made the checking vanish.
*/
is_prompt(dir, flag, success)
{
if (dir == SRCH_FORW) {
if (success != FALSE)
is_dspl("i-search forward", flag);
else
is_dspl("failing i-search forward", flag);
} else if (dir == SRCH_BACK) {
if (success != FALSE)
is_dspl("i-search backward", flag);
else
is_dspl("failing i-search backward", flag);
}
}
/*
* Prompt writing routine for the incremental search.
* The "prompt" is just a string. The "flag" determines
* if a "[ ]" or ":" embelishment is used.
*/
is_dspl(prompt, flag)
char *prompt;
{
if (flag != FALSE)
eprintf("%s [%s]", prompt, pat);
else
eprintf("%s: %s", prompt, pat);
}
/*
* Query Replace.
* Replace strings selectively. Does a search and replace operation.
* A space or a comma replaces the string, a period replaces and quits,
* an n doesn't replace, a C-G quits.
*/
queryrepl(f, n, k)
{
register int s;
char news[NPAT]; /* replacement string */
register int kludge; /* Watch for saved line move */
LINE *clp; /* saved line pointer */
int cbo; /* offset into the saved line */
int rcnt = 0; /* Replacements made so far */
int plen; /* length of found string */
if ((s=readpattern("Old string")) != TRUE)
return (s);
if ((s=ereply("New string: ",news, NPAT)) == ABORT)
return (s);
if (s == FALSE)
news[0] = '\0';
eprintf("Query Replace: [%s] -> [%s]", pat, news);
plen = strlen(pat);
/*
* Search forward repeatedly, checking each time whether to insert
* or not. The "!" case makes the check always true, so it gets put
* into a tighter loop for efficiency.
*
* If we change the line that is the remembered value of dot, then
* it is possible for the remembered value to move. This causes great
* pain when trying to return to the non-existant line.
*
* possible fixes:
* 1) put a single, relocated marker in the WINDOW structure, handled
* like mark. The problem now becomes a what if two are needed...
* 2) link markers into a list that gets updated (auto structures for
* the nodes)
* 3) Expand the mark into a stack of marks and add pushmark, popmark.
*/
clp = curwp->w_dotp; /* save the return location */
cbo = curwp->w_doto;
while (forwsrch() == TRUE) {
retry:
update();
switch (ttgetc()) {
case ' ':
case ',':
kludge = (curwp->w_dotp == clp);
if (lreplace(plen, news, f) == FALSE)
return (FALSE);
rcnt++;
if (kludge != FALSE)
clp = curwp->w_dotp;
break;
case '.':
kludge = (curwp->w_dotp == clp);
if (lreplace(plen, news, f) == FALSE)
return (FALSE);
rcnt++;
if (kludge != FALSE)
clp = curwp->w_dotp;
goto stopsearch;
case CCHR('G'):
ctrlg(FALSE, 0, KRANDOM);
goto stopsearch;
case '!':
do {
kludge = (curwp->w_dotp == clp);
if (lreplace(plen, news, f) == FALSE)
return (FALSE);
rcnt++;
if (kludge != FALSE)
clp = curwp->w_dotp;
} while (forwsrch() == TRUE);
goto stopsearch;
case 'n':
break;
default:
eprintf("<SP>[,] replace, [.] rep-end, [n] don't, [!] repl rest [C-G] quit");
goto retry;
}
}
stopsearch:
curwp->w_dotp = clp;
curwp->w_doto = cbo;
curwp->w_flag |= WFHARD;
update();
if (rcnt == 0)
eprintf("[No replacements done]");
else if (rcnt == 1)
eprintf("[1 replacement done]");
else
eprintf("[%d replacements done]", rcnt);
return (TRUE);
}
/*
* This routine does the real work of a
* forward search. The pattern is sitting in the external
* variable "pat". If found, dot is updated, the window system
* is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
forwsrch()
{
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register char *pp;
register int c;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
while (clp != curbp->b_linep) {
if (cbo == llength(clp)) {
clp = lforw(clp);
cbo = 0;
c = '\n';
} else
c = lgetc(clp, cbo++);
if (eq(c, pat[0]) != FALSE) {
tlp = clp;
tbo = cbo;
pp = &pat[1];
while (*pp != 0) {
if (tlp == curbp->b_linep)
goto fail;
if (tbo == llength(tlp)) {
tlp = lforw(tlp);
if (tlp == curbp->b_linep)
goto fail;
tbo = 0;
c = '\n';
} else
c = lgetc(tlp, tbo++);
if (eq(c, *pp++) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
fail: ;
}
return (FALSE);
}
/*
* This routine does the real work of a
* backward search. The pattern is sitting in the external
* variable "pat". If found, dot is updated, the window system
* is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
backsrch()
{
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register int c;
register char *epp;
register char *pp;
for (epp = &pat[0]; epp[1] != 0; ++epp)
;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
for (;;) {
if (cbo == 0) {
clp = lback(clp);
if (clp == curbp->b_linep)
return (FALSE);
cbo = llength(clp)+1;
}
if (--cbo == llength(clp))
c = '\n';
else
c = lgetc(clp,cbo);
if (eq(c, *epp) != FALSE) {
tlp = clp;
tbo = cbo;
pp = epp;
while (pp != &pat[0]) {
if (tbo == 0) {
tlp = lback(tlp);
if (tlp == curbp->b_linep)
goto fail;
tbo = llength(tlp)+1;
}
if (--tbo == llength(tlp))
c = '\n';
else
c = lgetc(tlp,tbo);
if (eq(c, *--pp) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
fail: ;
}
}
/*
* Compare two characters.
* The "bc" comes from the buffer.
* It has its case folded out. The
* "pc" is from the pattern.
*/
eq(bc, pc)
{
register int ibc;
register int ipc;
ibc = bc & 0xFF;
ipc = pc & 0xFF;
if (ISLOWER(ibc) != FALSE)
ibc = TOUPPER(ibc);
if (ISLOWER(ipc) != FALSE)
ipc = TOUPPER(ipc);
if (ibc == ipc)
return (TRUE);
return (FALSE);
}
/*
* Read a pattern.
* Stash it in the external variable "pat". The "pat" is
* not updated if the user types in an empty line. If the user typed
* an empty line, and there is no old pattern, it is an error.
* Display the old pattern, in the style of Jeff Lomicka. There is
* some do-it-yourself control expansion.
*/
readpattern(prompt)
char *prompt;
{
register int s;
char tpat[NPAT];
s = ereply("%s [%s]: ", tpat, NPAT, prompt, pat);
if (s == TRUE) /* Specified */
strcpy(pat, tpat);
else if (s==FALSE && pat[0]!=0) /* CR, but old one */
s = TRUE;
return (s);
}